﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Unity.Collections;
using Unity.VisualScripting;
using Unity.VisualScripting.FullSerializer;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UI;
using UnityEngine;

public enum ImpulseType: uint
{ 
    None = 0,
    Left = 1 << 0, 
    Right = 1 << 1,
    Up = 1 << 2,
    Down = 1 << 3,

    Moving = Left | Right | Up | Down,

    Stop = 1 << 4,
    FinishImpulse = (uint)1 << 30
}

public enum RelevanceType
{ 
    DontCare = 0,
    Left = 1 << 0,
    Right = 1 << 1,
    Up = 1 << 2,
    Down = 1 << 3,
}

/// <summary>
/// 抽象的Object，只保存名字，作为匹配的条件
/// </summary>
public struct AbstructObject
{
    public string Name;
}
/// <summary>
/// Object之间的关系，用Id索引Object
/// Relevance提供这两个Object的关系
/// </summary>
public struct AbstructObjectRef
{
    public AbstructObject Object;
    public ImpulseType Impulse;
}

public class RuleSegment: ICloneable
{
    public AbstructObjectRef[] AbstructObjectRefs;
    public RelevanceType Relevance;

    public object Clone()
    {
        RuleSegment OutRuleSegment = new RuleSegment();
        OutRuleSegment.Relevance = Relevance;
        OutRuleSegment.AbstructObjectRefs = (AbstructObjectRef[])AbstructObjectRefs.Clone();

        return OutRuleSegment;
    }

    public string ToMermaid()
    {
        string Output = "";

        switch (Relevance)
        {
            case RelevanceType.Left: Output += "(L)"; break;
            case RelevanceType.Right: Output += "(R)"; break;
            case RelevanceType.Up: Output += "(U)"; break;
            case RelevanceType.Down: Output += "(D)"; break;
        }
        Output += "[";
        for (int i = 0; i < AbstructObjectRefs.Length; i++)
        {
            if (i > 0) Output += "|";
            var Object = AbstructObjectRefs[i];

            switch (Object.Impulse)
            {
                case ImpulseType.Left: Output += "(L)"; break;
                case ImpulseType.Right: Output += "(R)"; break;
                case ImpulseType.Up: Output += "(U)"; break;
                case ImpulseType.Down: Output += "(D)"; break;
                case ImpulseType.Stop: Output += "(Stop)"; break;
            }

            Output += Object.Object.Name;
        }
        Output += "]";

        return Output;
    }
}

/// <summary>
/// 单个规则语句，其中包括LHS,RHS
/// 规则只包含规则，其与任何的Object实例都无关
/// 规则中包含了这条规则所有的名字，LHS与RHS都是对其的引用
/// LHS执行完了之后，会将Object实例Match到Solver
/// 
/// 先只考虑完全对准位置的情况下修改状态
/// </summary>
class Rule
{
    AbstructObject[] AbstructObjects;
    public RuleSegment LHS;
    public RuleSegment RHS;

    public Rule SetObjects(AbstructObject[] AbstructObjects)
    {
        this.AbstructObjects = (AbstructObject[])AbstructObjects.Clone();
        return this;
    }
    public Rule AddLHS(RuleSegment InLHS)
    {
        LHS = InLHS;
        return this;
    }
    public Rule AddRHS(RuleSegment InRHS)
    {
        RHS = InRHS;
        return this;
    }
}

public enum EdgeActiveType
{ 
    None = 0,
    Up,
    Left,
    Down,
    Right,
    Action,
    Match,
    Rule
}
public class StateMachineNode 
{
    public uint Index;
    public bool bIsLHS = false;
    public bool bIsRHS = false;
    public RuleSegment RuleSegment;
    public List<StateMachineEdge> Edges;
}
public class StateMachineEdge
{
    public uint FromNode;
    public uint ToNode;
    public EdgeActiveType ActiveType;
}
public class RuleStateMachine
{
    public List<StateMachineNode> Nodes;
    public List<StateMachineEdge> Edges;
    public Dictionary<int, int> NodeIndices;

    void AddRule(Rule Rule)
    {
        {
            StateMachineNode Node = new StateMachineNode();
            Node.Index = (uint)Nodes.Count;
            Node.RuleSegment = new RuleSegment();
            Node.RuleSegment = (RuleSegment)Rule.LHS.Clone();
            Node.bIsLHS = true;
            Nodes.Add(Node);
            NodeIndices[Nodes.Count - 1] = Nodes.Count - 1;
        }
        {
            StateMachineNode Node = new StateMachineNode();
            Node.Index = (uint)Nodes.Count;
            Node.RuleSegment = new RuleSegment();
            Node.RuleSegment = (RuleSegment)Rule.RHS.Clone();
            Node.bIsRHS = true;
            Nodes.Add(Node);
            NodeIndices[Nodes.Count - 1] = Nodes.Count - 1;
        }
        {
            StateMachineEdge Edge = new StateMachineEdge();
            Edge.FromNode = (uint)Nodes.Count - 2;
            Edge.ToNode = (uint)Nodes.Count - 1;
            Edge.ActiveType = EdgeActiveType.Rule;

            Edges.Add(Edge);
        }
    }

    /// <summary>
    /// 所有的LHS都可以被非LHS Match
    /// 非LHS --> LHS : Match
    /// </summary>
    public void AutoMatch()
    { 
        for (int i = 0; i < Nodes.Count; i++)
        {
            StateMachineNode FromNode = Nodes[i];
            if (FromNode.bIsLHS) { continue; }

            var bIsRHS = FromNode.bIsRHS;
            var EdgesCount = Edges.Count;
            StateMachineNode FromFromNode = Nodes[0];
            if (bIsRHS)
            {
                for (int j = 0; j < EdgesCount; j++) 
                {
                    if (Edges[j].ToNode == FromNode.Index && Edges[j].ActiveType == EdgeActiveType.Rule)
                    {
                        FromFromNode = Nodes[(int)Edges[j].FromNode];
                    }
                }           
            }

            for (int j = 0; j < EdgesCount; j++)
            {
                StateMachineEdge Edge = Edges[j];
                if (Edge.ActiveType != EdgeActiveType.Rule)
                {
                    continue;
                }
                StateMachineNode ToNode = Nodes[NodeIndices[(int)Edge.FromNode]];
                // 是否满足Match条件

                // 输出是否在输入里面有
                bool bHasConnection = false;
                for (int k = 0; k < FromNode.RuleSegment.AbstructObjectRefs.Length; k++)
                {
                    var Output = FromNode.RuleSegment.AbstructObjectRefs[k];

                    if (bIsRHS)
                    {
                        if (FromFromNode.RuleSegment.AbstructObjectRefs[k].Object.Name == Output.Object.Name && FromFromNode.RuleSegment.AbstructObjectRefs[k].Impulse == Output.Impulse)
                        {
                            continue;
                        }
                    }

                    for (int k2 = 0; k2 < ToNode.RuleSegment.AbstructObjectRefs.Length; k2++)
                    {
                        var Input = ToNode.RuleSegment.AbstructObjectRefs[k2];

                        bHasConnection = bHasConnection || (Output.Object.Name == Input.Object.Name && Output.Impulse == Input.Impulse);
                    }
                }
                if (!bHasConnection)
                {
                    continue;
                }

                // 输出是否存在已知阻挡
                bool bAllBlocked = true;
                if (FromNode.RuleSegment.Relevance == ToNode.RuleSegment.Relevance)
                {
                    for (int k = 0; k < FromNode.RuleSegment.AbstructObjectRefs.Length; k++)
                    {
                        var Output = FromNode.RuleSegment.AbstructObjectRefs[k];
                        for (int k2 = 0; k2 < ToNode.RuleSegment.AbstructObjectRefs.Length; k2++)
                        {
                            var Input = ToNode.RuleSegment.AbstructObjectRefs[k2];
                            bool bHasBlocked = false;
                            // 找到匹配进行前后搜索
                            if (Output.Object.Name == Input.Object.Name && Output.Impulse == Input.Impulse)
                            {
                                var Pointer1 = k;
                                var Pointer2 = k2;
                                // 负向查找
                                while (Pointer1 > 0 && Pointer2 > 0)
                                {
                                    var Pattern1 = FromNode.RuleSegment.AbstructObjectRefs[Pointer1];
                                    var Pattern2 = ToNode.RuleSegment.AbstructObjectRefs[Pointer2];
                                    bHasBlocked = bHasBlocked || Pattern1.Object.Name != Pattern2.Object.Name || Pattern1.Impulse != Pattern2.Impulse;
                                    Pointer1 -= 1;
                                    Pointer2 -= 1;
                                }
                                Pointer1 = k;
                                Pointer2 = k2;
                                // 正向查找
                                while (Pointer1 < FromNode.RuleSegment.AbstructObjectRefs.Length && Pointer2 < ToNode.RuleSegment.AbstructObjectRefs.Length)
                                {
                                    var Pattern1 = FromNode.RuleSegment.AbstructObjectRefs[Pointer1];
                                    var Pattern2 = ToNode.RuleSegment.AbstructObjectRefs[Pointer2];
                                    bHasBlocked = bHasBlocked || Pattern1.Object.Name != Pattern2.Object.Name || Pattern1.Impulse != Pattern2.Impulse;
                                    Pointer1 += 1;
                                    Pointer2 += 1;
                                }
                            }
                            bAllBlocked &= bHasBlocked;
                        }

                    }
                    if (bAllBlocked)
                    {
                        Debug.Log(FromNode.RuleSegment.ToMermaid() + " --> " + ToNode.RuleSegment.ToMermaid());
                        continue;
                    }
                }

                StateMachineEdge NewEdge = new StateMachineEdge();
                NewEdge.FromNode = FromNode.Index;
                NewEdge.ToNode = ToNode.Index;
                NewEdge.ActiveType = EdgeActiveType.Match;

                Edges.Add(NewEdge);
            }
        }
    }

    public string ToMermaid()
    {
        string Output = "stateDiagram\n";

        foreach (var Edge in Edges)
        {
            var FromNode = Nodes[NodeIndices[(int)Edge.FromNode]];
            var ToNode = Nodes[NodeIndices[(int)Edge.ToNode]];

            Output += FromNode.RuleSegment.ToMermaid() + " --> " + ToNode.RuleSegment.ToMermaid();
            switch (Edge.ActiveType)
            {
                case EdgeActiveType.Rule: Output += ": Rule"; break;
                case EdgeActiveType.Match: Output += ": Match"; break;
                case EdgeActiveType.Up: Output += ": W"; break;
                case EdgeActiveType.Down: Output += ": S"; break;
                case EdgeActiveType.Left: Output += ": A"; break;
                case EdgeActiveType.Right: Output += ": D"; break;
                case EdgeActiveType.None: Output += ": None"; break;
            }
            Output += "\n";
        }

        return Output;
    }

    internal RuleStateMachine AddRules(Rule[] Rules)
    {
        foreach (Rule Rule in Rules)
        {
            AddRule(Rule);
        }
        return this;
    }
}

public class PuzzleRuleSystem
{
    public RuleStateMachine StateMachine;
    struct RuleTemp {
        public string Name;
        public ImpulseType FromImpulse;
        public ImpulseType ToImpulse;

        public RuleTemp(string InName, ImpulseType InFromImpulse, ImpulseType InToImpulse)
        {
            Name = InName;
            FromImpulse = InFromImpulse;
            ToImpulse = InToImpulse;
        }
    }

    Rule[] ToRule(RuleTemp[] Objects)
    {
        List<Rule> OutRules = new List<Rule> ();
        foreach (int Relevance in Enum.GetValues(typeof(RelevanceType))) 
        {
            RelevanceType RelevanceEnum = (RelevanceType)Relevance;
            ImpulseType ImpulseEnum = (ImpulseType)(int)Relevance;

            if (RelevanceEnum == 0) { continue; }

            var Rule = new Rule();
            RuleSegment LHS = new RuleSegment();
            LHS.AbstructObjectRefs = new AbstructObjectRef[Objects.Length];
            for (int i = 0; i < Objects.Length; i++)
            {
                LHS.AbstructObjectRefs[i].Object.Name = Objects[i].Name;
                LHS.AbstructObjectRefs[i].Impulse = Objects[i].FromImpulse == ImpulseType.Moving ? Objects[i].FromImpulse & ImpulseEnum : Objects[i].FromImpulse;
            }
            LHS.Relevance = RelevanceEnum;

            RuleSegment RHS = new RuleSegment();
            RHS.AbstructObjectRefs = new AbstructObjectRef[Objects.Length];
            for (int i = 0; i < Objects.Length; i++)
            {
                RHS.AbstructObjectRefs[i].Object.Name = Objects[i].Name;
                RHS.AbstructObjectRefs[i].Impulse = Objects[i].ToImpulse == ImpulseType.Moving ? Objects[i].ToImpulse & ImpulseEnum : Objects[i].ToImpulse;
            }
            RHS.Relevance = RelevanceEnum;

            Rule.AddLHS(LHS).AddRHS(RHS);

            OutRules.Add(Rule);
        }

        return OutRules.ToArray();
    }
    public void Gen()
    {
        StateMachine = new RuleStateMachine();
        StateMachine.Nodes = new List<StateMachineNode>();
        StateMachine.Edges = new List<StateMachineEdge>();
        StateMachine.NodeIndices = new Dictionary<int, int>();
        // Fix
        {
            StateMachineNode RootNode = new StateMachineNode();
            RootNode.Index = 0;
            RootNode.RuleSegment = new RuleSegment();
            RootNode.RuleSegment.AbstructObjectRefs = new AbstructObjectRef[0];

            StateMachine.Nodes.Add(RootNode);
            StateMachine.NodeIndices[0] = StateMachine.Nodes.Count - 1;
        }
        {
            StateMachineNode Node = new StateMachineNode();
            Node.Index = 1;
            Node.RuleSegment = new RuleSegment();
            Node.RuleSegment.AbstructObjectRefs = new AbstructObjectRef[1];
            Node.RuleSegment.AbstructObjectRefs[0].Object.Name = "Player";
            Node.RuleSegment.AbstructObjectRefs[0].Impulse = ImpulseType.None;
            Node.RuleSegment.Relevance = RelevanceType.DontCare;

            StateMachine.Nodes.Add(Node);
            StateMachine.NodeIndices[1] = StateMachine.Nodes.Count - 1;
        }
        {
            StateMachineEdge Edge = new StateMachineEdge();
            Edge.FromNode = 0;
            Edge.ToNode = 1;
            Edge.ActiveType = EdgeActiveType.None;

            StateMachine.Edges.Add(Edge);
        }
        // Player
        {
            StateMachineNode Node = new StateMachineNode();
            Node.Index = 2;
            Node.RuleSegment = new RuleSegment();
            Node.RuleSegment.AbstructObjectRefs = new AbstructObjectRef[1];
            Node.RuleSegment.AbstructObjectRefs[0].Object.Name = "Player";
            Node.RuleSegment.AbstructObjectRefs[0].Impulse = ImpulseType.Right;
            Node.RuleSegment.Relevance = RelevanceType.DontCare;

            StateMachine.Nodes.Add(Node);
            StateMachine.NodeIndices[2] = StateMachine.Nodes.Count - 1;
        }
        {
            StateMachineEdge Edge = new StateMachineEdge();
            Edge.FromNode = 1;
            Edge.ToNode = 2;
            Edge.ActiveType = EdgeActiveType.Right;

            StateMachine.Edges.Add(Edge);
        }

        {
            StateMachineNode Node = new StateMachineNode();
            Node.Index = 3;
            Node.RuleSegment = new RuleSegment();
            Node.RuleSegment.AbstructObjectRefs = new AbstructObjectRef[1];
            Node.RuleSegment.AbstructObjectRefs[0].Object.Name = "Player";
            Node.RuleSegment.AbstructObjectRefs[0].Impulse = ImpulseType.Left;
            Node.RuleSegment.Relevance = RelevanceType.DontCare;

            StateMachine.Nodes.Add(Node);
            StateMachine.NodeIndices[3] = StateMachine.Nodes.Count - 1;
        }
        {
            StateMachineEdge Edge = new StateMachineEdge();
            Edge.FromNode = 1;
            Edge.ToNode = 3;
            Edge.ActiveType = EdgeActiveType.Left;

            StateMachine.Edges.Add(Edge);
        }

        {
            StateMachineNode Node = new StateMachineNode();
            Node.Index = 4;
            Node.RuleSegment = new RuleSegment();
            Node.RuleSegment.AbstructObjectRefs = new AbstructObjectRef[1];
            Node.RuleSegment.AbstructObjectRefs[0].Object.Name = "Player";
            Node.RuleSegment.AbstructObjectRefs[0].Impulse = ImpulseType.Up;
            Node.RuleSegment.Relevance = RelevanceType.DontCare;

            StateMachine.Nodes.Add(Node);
            StateMachine.NodeIndices[4] = StateMachine.Nodes.Count - 1;
        }
        {
            StateMachineEdge Edge = new StateMachineEdge();
            Edge.FromNode = 1;
            Edge.ToNode = 4;
            Edge.ActiveType = EdgeActiveType.Up;

            StateMachine.Edges.Add(Edge);
        }

        {
            StateMachineNode Node = new StateMachineNode();
            Node.Index = 5;
            Node.RuleSegment = new RuleSegment();
            Node.RuleSegment.AbstructObjectRefs = new AbstructObjectRef[1];
            Node.RuleSegment.AbstructObjectRefs[0].Object.Name = "Player";
            Node.RuleSegment.AbstructObjectRefs[0].Impulse = ImpulseType.Down;
            Node.RuleSegment.Relevance = RelevanceType.DontCare;

            StateMachine.Nodes.Add(Node);
            StateMachine.NodeIndices[5] = StateMachine.Nodes.Count - 1;
        }
        {
            StateMachineEdge Edge = new StateMachineEdge();
            Edge.FromNode = 1;
            Edge.ToNode = 5;
            Edge.ActiveType = EdgeActiveType.Down;

            StateMachine.Edges.Add(Edge);
        }

        StateMachine
            .AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Player", ImpulseType.Moving, ImpulseType.Moving),
                    new RuleTemp("Box", ImpulseType.None, ImpulseType.Moving),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Player", ImpulseType.Moving, ImpulseType.Stop),
                    new RuleTemp("Wall", ImpulseType.None, ImpulseType.None),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Box", ImpulseType.Moving, ImpulseType.Stop),
                    new RuleTemp("Wall", ImpulseType.None, ImpulseType.None),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Player", ImpulseType.Moving, ImpulseType.Stop),
                    new RuleTemp("Box", ImpulseType.Stop, ImpulseType.Stop),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Box", ImpulseType.Moving, ImpulseType.Moving),
                    new RuleTemp("Box", ImpulseType.None, ImpulseType.Moving),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Box", ImpulseType.Moving, ImpulseType.Stop),
                    new RuleTemp("Box", ImpulseType.Stop, ImpulseType.Stop),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Box", ImpulseType.Moving, ImpulseType.Moving),
                    new RuleTemp("Box2", ImpulseType.None, ImpulseType.Moving),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Player", ImpulseType.Moving, ImpulseType.Moving),
                    new RuleTemp("Box2", ImpulseType.None, ImpulseType.Moving),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Box2", ImpulseType.Moving, ImpulseType.Stop),
                    new RuleTemp("Wall", ImpulseType.None, ImpulseType.None),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Box2", ImpulseType.Moving, ImpulseType.Stop),
                    new RuleTemp("Box", ImpulseType.None, ImpulseType.None),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Box2", ImpulseType.Moving, ImpulseType.Stop),
                    new RuleTemp("Box", ImpulseType.Stop, ImpulseType.Stop),
                })
            ).AddRules(
                ToRule(new RuleTemp[] {
                    new RuleTemp("Player", ImpulseType.Moving, ImpulseType.Stop),
                    new RuleTemp("Box2", ImpulseType.Stop, ImpulseType.Stop),
                })
            );

        StateMachine.AutoMatch();
        for (int i = 0; i < StateMachine.Nodes.Count; i++)
        {
            var Node = StateMachine.Nodes[i];
            Node.Edges = new List<StateMachineEdge>();
            for (int j = 0; j < StateMachine.Edges.Count; j++)
            {
                var Edge = StateMachine.Edges[j];
                if (Edge.FromNode == Node.Index)
                {
                    Node.Edges.Add(Edge);
                }
            }
        }

        Debug.Log(StateMachine.ToMermaid());
    }

    public void Generate()
    {
        Rule TestSegment = new Rule();
        AbstructObject[] AbstructObjects = new AbstructObject[2];
        AbstructObjects[0].Name = "Player";
        AbstructObjects[1].Name = "Box"; 

        RuleSegment LHS = new RuleSegment();
        LHS.AbstructObjectRefs = new AbstructObjectRef[2];
        LHS.AbstructObjectRefs[0].Object.Name = "Player";
        LHS.AbstructObjectRefs[0].Impulse = ImpulseType.Right;
        LHS.AbstructObjectRefs[1].Object.Name = "Box";
        LHS.AbstructObjectRefs[1].Impulse = ImpulseType.None;
        LHS.Relevance = RelevanceType.Right;

        RuleSegment RHS = new RuleSegment();
        RHS.AbstructObjectRefs = new AbstructObjectRef[2];
        RHS.AbstructObjectRefs[0].Object.Name = "Player";
        RHS.AbstructObjectRefs[0].Impulse = ImpulseType.Right;
        RHS.AbstructObjectRefs[1].Object.Name = "Box";
        RHS.AbstructObjectRefs[1].Impulse = ImpulseType.Right;
        RHS.Relevance = RelevanceType.Right;

        TestSegment
            .SetObjects(AbstructObjects)
            .AddLHS(LHS).AddRHS(RHS);
    }

    // 可能需要执行的检查
    // 1. FromNode一定需要信息，除了RootNode
    // 2. 左右两边的方向需要对应
    public void GenerateStateMachine()
    {

        
        
    }

    public void DumpStateMachine()
    {
        for (int NodeIndex = 0; NodeIndex < StateMachine.Nodes.Count; NodeIndex++)
        {
            //Debug.Log(NodeIndex);
        }
        for (int EdgeIndex = 0; EdgeIndex < StateMachine.Edges.Count; EdgeIndex++)
        {
            //Debug.Log(EdgeIndex);
        }
    }
}
